package com.css.util;
/**
 * @author create by Mr.X
 * @Date 2021/7/23- 9:58
 **/


import cn.hutool.core.util.StrUtil;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.*;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.xwpf.usermodel.*;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;


public class WordUtil {
    private String[][] wordTable;
    private OutputStream os; //导出输出文件流
    private final String templateFilePath = "C:\\Users\\xinyue\\Desktop\\template\\template.doc";

    /******************************************************导入**********************************************/

    /**
     * 根据word文本获取二维表
     * @param filePath  doc/docx文件路径
     * @param orderNum  指定读取的doc内table表的序号
     * @return
     */
    public String[][] getTableByword(String filePath, Integer orderNum){
        createTableByWord(filePath,orderNum,20);
        return this.wordTable;
    }

    /**
     * 根据word文本获取二维表
     * @param file  doc/docx文件
     * @param orderNum  指定读取的doc内table表的序号
     * @return
     */
    public String[][] getTableByword(File file, Integer orderNum){
        createTableByWord(file.getAbsolutePath(),orderNum,20);
        return this.wordTable;
    }

    /**
     * 根据word文本获取二维表
     * @param filePath  doc/docx文件路径
     * @param orderNum  指定读取的doc内table表的序号
     * @param columnMaxNum  最大列数
     * @return
     */
    public String[][] getTableByword(String filePath, Integer orderNum, Integer columnMaxNum){
        createTableByWord(filePath,orderNum,columnMaxNum);
        return this.wordTable;
    }

    /**
     * 构建二维表
     * @param x  x轴坐标
     * @param y  y轴坐标
     * @param v        参数值
     * @return
     */
    private void buildWordTable(int x, int y, String v){
        if(StrUtil.isEmpty(wordTable[x][y])){
            this.wordTable[x][y] = v;
        }else{
            StringBuilder sb = new StringBuilder(wordTable[x][y].toString()).append("\r\n").append(v);
            this.wordTable[x][y] = sb.toString();
        }
    }

    /**
     * 读取doc/docx 转table
     * @param filePath 文档路径
     * @param orderNum 设置需要读取的第几个表格
     * @param columnMaxNum  二维表列数
     */
    private void createTableByWord(String filePath, Integer orderNum, Integer columnMaxNum){
        FileInputStream in = null;
        try{
            in = new FileInputStream(filePath);
            if(filePath.toLowerCase().endsWith("doc")){
                //doc情况
                POIFSFileSystem pfs = new POIFSFileSystem(in);
                HWPFDocument hwpf = new HWPFDocument(pfs);
                Range range = hwpf.getRange();
                TableIterator itpre = new TableIterator(range);
                int total = 0;
                while (itpre.hasNext()) {
                    itpre.next();
                    total += 1;
                }
                TableIterator it = new TableIterator(range);
                int set = orderNum;
                int num = set;
                for (int i = 0; i < set-1; i++) {
                    it.hasNext();
                    it.next();
                }
                while (it.hasNext()) {
                    Table tb = (Table) it.next();
                    this.wordTable = new String[tb.numRows()][columnMaxNum];
                    for (int i = 0; i < tb.numRows(); i++) {
                        TableRow tr = tb.getRow(i);
                        for (int j = 0; j < tr.numCells(); j++) {
                            TableCell td = tr.getCell(j);//取得单元格
                            for(int k = 0; k < td.numParagraphs(); k++){
                                Paragraph para = td.getParagraph(k);
                                String s = para.text();
                                if(null != s && !"".equals(s)){
                                    s = s.substring(0, s.length()-1);
                                }
                                buildWordTable(i,j,s);
                            }
                        }
                    }
                    while (num < total) {
                        it.hasNext();
                        it.next();
                        num += 1;
                    }
                }
            }else{
                //docx情况
                //word 2007 图片不会被读取， 表格中的数据会被放在字符串的最后
                XWPFDocument xwpf = new XWPFDocument(in);//得到word文档的信息
                Iterator<XWPFTable> itpre = xwpf.getTablesIterator();//得到word中的表格
                int total = 0;
                while (itpre.hasNext()) {
                    itpre.next();
                    total += 1;
                }
                Iterator<XWPFTable> it = xwpf.getTablesIterator();//得到word中的表格
                // 设置需要读取的表格  set是设置需要读取的第几个表格，total是文件中表格的总数
                int set = orderNum;
                int num = set;
                // 过滤前面不需要的表格
                for (int i = 0; i < set-1; i++) {
                    it.hasNext();
                    it.next();
                }
                while(it.hasNext()){
                    XWPFTable table = it.next();
                    //初始化二维表容器
                    this.wordTable = new String[table.getNumberOfRows()][columnMaxNum];
                    List<XWPFTableRow> rows = table.getRows();
                    //读取每一行数据
                    for (int i = 0; i < rows.size(); i++) {
                        XWPFTableRow row = rows.get(i);
                        //读取每一列数据
                        List<XWPFTableCell> cells = row.getTableCells();
                        for (int j = 0; j < cells.size(); j++) {
                            XWPFTableCell cell = cells.get(j);
                            buildWordTable(i,j,cell.getText());
                        }
                    }
                    // 过滤多余的表格
                    while (num < total) {
                        it.hasNext();
                        it.next();
                        num += 1;
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(in!=null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

/******************************************************导出************************************************/
    /**
     * 替换所有段落中的标记
     *
     * @param xwpfParagraphList
     * @param params
     */
    public static void replaceInAllParagraphs(List<XWPFParagraph> xwpfParagraphList, Map<String, String> params) {
        for (XWPFParagraph paragraph : xwpfParagraphList) {
            if (paragraph.getText() == null || paragraph.getText().equals("")) continue;
            for (String key : params.keySet()) {
                if (paragraph.getText().contains(key)) {
                    replaceInParagraph(paragraph, key, params.get(key));
                }
            }
        }
    }

    /**
     * 替换段落中的字符串
     *
     * @param xwpfParagraph
     * @param oldString
     * @param newString
     */
    public static void replaceInParagraph(XWPFParagraph xwpfParagraph, String oldString, String newString) {
        Map<String, Integer> pos_map = findSubRunPosInParagraph(xwpfParagraph, oldString);
        if (pos_map != null) {
            List<XWPFRun> runs = xwpfParagraph.getRuns();
            XWPFRun modelRun = runs.get(pos_map.get("end_pos"));
            XWPFRun xwpfRun = xwpfParagraph.insertNewRun(pos_map.get("end_pos") + 1);
            if(newString.contains("\r\n")){
                //解决poi导出docx \r\n 无法换行问题
                String[] split = newString.split("\r\n");
                if (split != null && split.length > 0) {
                    for (int i = 0; i < split.length; i++) {
                        xwpfRun.setText(" " + split[i].trim());
                        if (i < (split.length - 1)) {
                            //换行
                            xwpfRun.addBreak();
                            //xwpfRun.addCarriageReturn();
                            //首行缩进
                            //xwpfRun.addTab();
                        }
                    }
                }
            }else{
                xwpfRun.setText(newString);
            }
            if (modelRun.getFontSize() != -1)
                xwpfRun.setFontSize(modelRun.getFontSize());//默认值是五号字体,但五号字体getFontSize()时,返回-1
            xwpfRun.setFontFamily(modelRun.getFontFamily());
            for (int i = pos_map.get("end_pos"); i >= pos_map.get("start_pos"); i--) {
                xwpfParagraph.removeRun(i);
            }
        }
    }

    /**
     * 找到段落中子串的起始XWPFRun下标和终止XWPFRun的下标
     *
     * @param xwpfParagraph
     * @param substring
     * @return
     */
    public static Map<String, Integer> findSubRunPosInParagraph(XWPFParagraph xwpfParagraph, String substring) {
        List<XWPFRun> runs = xwpfParagraph.getRuns();
        int start_pos = 0;
        int end_pos = 0;
        String subtemp = "";
        for (int i = 0; i < runs.size(); i++) {
            subtemp = "";
            start_pos = i;
            for (int j = i; j < runs.size(); j++) {
                if (runs.get(j).getText(runs.get(j).getTextPosition()) == null) continue;
                subtemp += runs.get(j).getText(runs.get(j).getTextPosition());
                if (subtemp.equals(substring)) {
                    end_pos = j;
                    Map<String, Integer> map = new HashMap<>();
                    map.put("start_pos", start_pos);
                    map.put("end_pos", end_pos);
                    return map;
                }
            }
        }
        return null;
    }

    /**
     * 替换所有的表格
     *
     * @param xwpfTableList
     * @param params
     */
    public static void replaceInTables(List<XWPFTable> xwpfTableList, Map<String, String> params) {
        for (XWPFTable table : xwpfTableList) {
            replaceInTable(table, params);
        }
    }

    /**
     * 替换一个表格中的所有行
     *
     * @param xwpfTable
     * @param params
     */
    public static void replaceInTable(XWPFTable xwpfTable, Map<String, String> params) {
        List<XWPFTableRow> rows = xwpfTable.getRows();
        replaceInRows(rows, params);
    }

    /**
     * 替换表格中的一行
     *
     * @param rows
     * @param params
     */
    public static void replaceInRows(List<XWPFTableRow> rows, Map<String, String> params) {
        for (int i = 0; i < rows.size(); i++) {
            XWPFTableRow row = rows.get(i);
            replaceInCells(row.getTableCells(), params);
        }
    }

    /**
     * 替换一行中所有的单元格
     *
     * @param xwpfTableCellList
     * @param params
     */
    public static void replaceInCells(List<XWPFTableCell> xwpfTableCellList, Map<String, String> params) {
        for (XWPFTableCell cell : xwpfTableCellList) {
            replaceInCell(cell, params);
        }
    }

    /**
     * 替换表格中每一行中的每一个单元格中的所有段落
     *
     * @param cell
     * @param params
     */
    public static void replaceInCell(XWPFTableCell cell, Map<String, String> params) {
        List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
        replaceInAllParagraphs(cellParagraphs, params);
    }

    public static void main(String[] args) {
        Map<String, String> params = new HashMap<>();
        params.put("${name}", "邹某");
        params.put("${nation}","汉族");
        params.put("${sex}","男");
        params.put("${birthday}",new SimpleDateFormat("yyyy.MM.dd").format(new Date()).toString());
        String filepath = "C:\\Users\\xinyue\\Desktop\\template\\template2.docx";
        String destpath = "C:\\Users\\xinyue\\Desktop\\template\\target.docx";
        WordUtil wordUtil = new WordUtil();
        try {
            OPCPackage pack = POIXMLDocument.openPackage(filepath);//读取模板文件
            XWPFDocument document = new XWPFDocument(pack);//创建word文件并将模板导入
            //对段落中的标记进行替换
            // List<XWPFParagraph> parasList = document.getParagraphs();
            // replaceInAllParagraphs(parasList, params);
            //表格标记替换
            List<XWPFTable> tables = document.getTables();
            wordUtil.replaceInTables(tables, params);
            FileOutputStream outStream = new FileOutputStream(destpath);
            document.write(outStream);
            outStream.flush();
            outStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}